NVIC_SYSPRI_14 EQU 0xE000ED22
ISCR EQU 0xE000ED04
PendSV EQU 0x10000000
	AREA SWITCH,CODE,READONLY,ALIGN=2
    PRESERVE8
	EXPORT OSStartHighRdy
	EXPORT OSCtxSw
	EXPORT OSIntCtxSw
	EXPORT OS_CPU_SR_Save
	EXPORT OS_CPU_SR_Restore
	IMPORT OSTimeTick
	IMPORT OSTCBCur
	IMPORT OSPrioHighRdy
	IMPORT OSTCBHighRdy
	IMPORT OSPrioCur
	IMPORT OSRunning
	IMPORT OSTaskSwHook
	IMPORT OS_CPU_ExceptStkBase
	IMPORT OSIntNesting
	ENTRY
OS_CPU_SR_Save
	CPSID I                 ;关闭中断，I位只是关中断，F位只是关快中断，IF则是中断和快中断都关，但是在ARM cortex-M系列由于提供了另一种机制
							;所以去除了FIQ模式，也就不需要都关
	PUSH {R1}				;压栈，我们使用R1作为一个中介
	MRS R1,BASEPRI			;当前状态(被中断的任务/被嵌套的中断)中BASEPRI,与S3C2440直接保存PSR不同
	MSR BASEPRI,R0			;由于我们传参进来，所以需要通过R0把参数给BASEPRI
	DSB						
	ISB
	MOV R0,R1				;把原来状态的BASEPRI作为返回值传给当前中断的cpu_sr
	POP {R1}				;出栈
	CPSIE I					;开中断
	BX LR					;返回执行
	
OS_CPU_SR_Restore
	CPSID I
	MSR BASEPRI,R0			;这里我们使用当前中断的cpu_sr作为参数传递以恢复之前的BASEPRI，与S3C2440恢复PSR不同
	DSB
	ISB
	CPSIE I
	BX LR
	
;CortexM_Save
	;LDR R0,=OSIntNesting
	;LDR R0,[R0]
	;ADD R0,R0,#1
	;TEQ R0,#1
	;BNE %F1
	
	;MRS R1,PSP
	;STMFD R1!,{R4-R11}
	;MSR PSP,R1

;1	MRS R1,MSP
	;STMFD R1!,{R4-R11}
	;MSR MSP,R1
	
	;BX LR
	
	
OSStartHighRdy
	
;初始化MSP.PSP.PendSV的优先级，定义PendSV为最低优先级
	LDR R0,=NVIC_SYSPRI_14
	MOV R1,#0xFF
	STRB R1,[R0] ;定义优先级为255，也就是所能支持的最低优先级
	
	;初始化PSP为0，以告知Switcher这是第一次任务开始
	MOV R0,#0
	MSR PSP,R0
	
	;初始化MSP为OS_CPU_ExceptStkBase
	LDR R0,=OS_CPU_ExceptStkBase
	LDR R0,[R0]
	MSR MSP,R0
	
	BL OSTaskSwHook
	
	;设置OSRunning为TRUE
	LDR R0, =OSRunning
	MOV R1, #1
	STRB R1, [R0]
	
	;我们在前面的步骤已经确定了OSPrioCur和OSTCBCur
	;我们取出我们自己定义的栈的地址赋给PSP，以作为接下来的操作的栈指针
	LDR R0,=OSTCBCur
	LDR R0,[R0]
	LDR R0,[R0]
	MSR PSP,R0
	
	;由于正常情况下两种程序运行模式都是默认使用MSP，但是正常程序运行最好使用PSP
	;但是在此处不确定是否可以对CONTROL寄存器进行操作，而且也不知道现在Thread状态下是不是特权模式
	MRS R0,CONTROL
	ORR R0,R0,#0x10 ;使用PSP
	MSR CONTROL,R0
	ISB
	;初始化之后由于是初次开始，要进行以此特殊的上下文切换
	;让第一个任务开始执行，这个函数不是异常，所以硬件不会自动帮我们压栈，需要在这里直接手动进行压栈
	LDMFD SP!,{R4-R11,LR}
	LDMFD SP!,{R0-R3}
	LDMFD SP!,{R12,LR}
	LDMFD SP!,{R1,R2};R1存储了PC，R2存储了CPSR的值
	
	
	;PC赋值
	BX R1 ;由于在最开始的时候我们模拟压栈时就确定了程序入口，所以出栈给PC可以直接开始执行，CPSR的值我们设置为了SVC模式，由于正常函数执行也不是异常，所以选出的程序处于Thread模式下的特权模式

PendSV_Handler
	MRS R0,PSP
	STMFD R0!,{R4-R11,R14};保存剩余现场,这里就包含了EXC_RETURN，在退出异常时，可以确定接下来的模式和栈指针
	
	LDR R2,=OSTCBCur
	LDR R1,[R2]
	STR R0,[R1];当前状态下，R0就是SP，需要存入TCB块
	
	;在这里维护EXC_RETURN
	MOV R4,LR
	BL OSTaskSwHook
	ORR LR,R4,#0x04 ;保证异常返回使用PSP
	
	;然后切换OSTCBCur
	;①进行OSPrioCur的切换，由于我们之前已经通过OS_SchedNew()改变了当前的OSPrioHighRdy
	;OSPrioCur = OSPrioHighRdy
	LDR R0,=OSPrioCur
	LDR R1,=OSPrioHighRdy
	LDRB R3,[R1];为什么在此处用LDRB而不是LDR呢，主要是因为优先级都是INT8U，为了防止其扩展为32位读取其他区域，我们采取字节的方式写入
	STRB R3,[R0];同理
	
	;②我们进行OSTCBCur的改变，同样此处的OSTCBHighRdy已经在OS_Sched()的前面步骤中获得
	;但是为了后续的切换，每一次还是需要更新OSTCBCur的
	LDR R0,=OSTCBCur
	LDR R1,=OSTCBHighRdy
	LDR R2,[R1]
	STR R2,[R0]
	
	;然后获取当前最高优先级任务的栈指针
	LDR R0,[R0]
	LDR R0,[R0]
	
	;然后正式开始恢复现场，也就是接下来的任务的现场恢复
	LDMFD R0!,{R4-R11,R14};异常中也就不需要进行对剩余的8个寄存器进行手动出栈了，硬件帮助我们完成
	;更新一下PSP，出栈后其实按理来说不知道应该时进入什么模式，所以其实应该在OSTaskSwHook中决定返回后的指针使用，而不是在这里使用MSR PSP,R0，这样的话如果进入HANDLER模式的话，可能MSP的值不对，影响后续操作
	;但是由于当前任务还较为简单，所以直接使用此方法也无所谓
	MSR PSP,R0;关于这里为什么不存一下到OSTCBCur->OSTCBStkPtr，可以看到我们上面进入的时候就是直接使用的PSP，也没有找OSTCBCur的首个字段值，所以我们在这里也就不需要去存
			  ;在后续的操作结束后呢，我们默认其PSP恰好回到了我们恢复时的位置
	
	BX LR;这时的LR难道没有被上面的覆盖吗？感觉和之前所说的压入PC和LR的值在此时相同有关

;OSIntCtxSw
	;BL OSTaskSwHook
	
	;LDR R0,=OSPrioHighRdy
	;LDR R1,=OSPrioCur
	;LDR R0,[R0]
	;STR R0,[R1]
	
	;LDR R0,=OSTCBHighRdy
	;LDR R1,=OSTCBCur
	;LDR R0,[R0]
	;STR R0,[R1]
	
	;LDR R1,[R1]
	;LDR R1,[R1]
	;LDMFD R1!,{R4-R11,LR}
	
	;MSR PSP,R1
	
	;BX LR

OSIntCtxSw
OSCtxSw
	LDR R0,=ISCR
	LDR R1,[R0]
	ORR R1,R1,#PendSV
	STR R1,[R0];软件触发PendSV_Handler
	BX LR
	
	ALIGN
	END
		
	
	